#include <linux/module.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/string.h>

#define YESNO_MAGIC 232535
#define LEVELS 5

MODULE_LICENSE("GPL");

static struct inode *yesno_get_inode(struct super_block *, int);


static struct dentry *yesno_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
	/* TBD */
	struct inode *tmp1;  
	if (LEVELS > *((int*)dir->i_private))
	{
	 if(strcmp(dentry->d_name.name,"yes") == 0) 
	 {
	   tmp1 = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3)+1);
     *((int*)tmp1->i_private)=*((int*)dir->i_private)+1;
     d_add(dentry, tmp1);
	 }
	 if(strcmp(dentry->d_name.name,"no") == 0) 
	 {
	   tmp1 = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3)+2);
     *((int*)tmp1->i_private)=*((int*)dir->i_private)+1;
     d_add(dentry, tmp1);
	 }
	 if(strcmp(dentry->d_name.name,"dunno") == 0) 
	 {
	   tmp1 = yesno_get_inode(dir->i_sb, ((dir->i_ino)*3)+3);
     *((int*)tmp1->i_private)=*((int*)dir->i_private)+1;
     d_add(dentry, tmp1);
	 }
	}
	return NULL;
}


static const struct inode_operations yesno_dir_inode_operations = {
	.lookup		= yesno_lookup,
};

static int yesno_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	/* TBD */
struct dentry *myden = filp->f_dentry;
	if(filp->f_pos > 0 )
                return -1;
  if (filldir(dirent, ".", 1, filp->f_pos++, myden->d_inode->i_ino, DT_DIR)||(filldir(dirent, "..", 2, filp->f_pos++, myden->d_parent->d_inode->i_ino, DT_DIR)))
                 return 0;
  if(filldir(dirent, "yes", 3, filp->f_pos++, ((myden->d_inode->i_ino)*3+1), DT_DIR ))
                return 0;
  if(filldir(dirent, "no", 2, filp->f_pos++, ((myden->d_inode->i_ino)*3+2), DT_DIR ))
                return 0;
  if(filldir(dirent, "dunno", 5, filp->f_pos++, ((myden->d_inode->i_ino)*3+3), DT_DIR ))
                return 0;
  return 1;
}


static const struct file_operations yesno_dir_operations = {
	.readdir = 	yesno_readdir,
};


struct inode *yesno_get_inode(struct super_block *sb, int num)
{
	struct inode *inode = new_inode(sb);

	if (inode) {
		inode->i_mode = S_IFDIR | 0555;
		inode->i_uid = 0;
		inode->i_gid = 0;
		inode->i_blocks = 0;
		inode->i_op = &yesno_dir_inode_operations;
		inode->i_fop = &yesno_dir_operations;
		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
		inc_nlink(inode);
		inode->i_ino = num;
	}
	return inode;
}

static struct super_operations yesno_ops = {
	.statfs 	= simple_statfs,
};

static int yesno_fill_super(struct super_block *sb, void *data, int silent)
{
	struct inode *inode;
	struct dentry *root;

	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = YESNO_MAGIC;
	sb->s_op = &yesno_ops;
	sb->s_time_gran = 1;
	inode = yesno_get_inode(sb, 1);
	if (!inode)
		return -ENOMEM;
	root = d_make_root(inode);
	if (!root) {
		iput(inode);
		return -ENOMEM;
	}
	sb->s_root = root;
	return 0;
}

struct dentry *yesno_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
	return mount_bdev(fs_type, flags, data, yesno_fill_super,mnt);
}

static struct file_system_type yesno_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "yesno",
  .mount		= yesno_mount,
	.kill_sb	= kill_anon_super,
};






static int __init init_dfs(void)
{
	return register_filesystem(&yesno_fs_type);
}

static void __exit exit_dfs(void)
{
	unregister_filesystem(&yesno_fs_type);
}

module_init(init_dfs)
module_exit(exit_dfs)
